First of all, we need to install some packages. Remember that dplyr lives in the tidyverse:

install.packages("tidyverse") 
install.packages("knitr")
install.packages("scales")
install.packages("ggthemes")
install.packages("highcharter")

And since this is a Pokemon-based exercise, let’s also install some Pokemon-related color palettes:

install.packages('palettetown')

Let’s load all packages:

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -------------------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
library(knitr)
library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(ggthemes)
library(palettetown)
library(highcharter, quietly = TRUE)
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
library(rvest)
Loading required package: xml2

Attaching package: ‘rvest’

The following object is masked from ‘package:readr’:

    guess_encoding

Now, let’s load that freely available Pokemon dataset!

data_file <- 'https://assets.datacamp.com/production/course_1815/datasets/Pokemon.csv'
data <- read_csv(data_file)
Parsed with column specification:
cols(
  Number = col_integer(),
  Name = col_character(),
  Type1 = col_character(),
  Type2 = col_character(),
  Total = col_integer(),
  HitPoints = col_integer(),
  Attack = col_integer(),
  Defense = col_integer(),
  SpecialAttack = col_integer(),
  SpecialDefense = col_integer(),
  Speed = col_integer(),
  Generation = col_integer(),
  Legendary = col_character()
)

And some more things happening under da hood:

r_90_d <- theme(axis.text.x = element_text(angle = 90, hjust = 1))
caption <- "RLadies Munich"
my_theme <- theme_few() 

We also need some Pokemon- and RLadies- related colors. Rattata seems to have a nice color scheme similar to both. We’ll use the %>% (pipe) operator from the magritte package (don’t worry. The tidyverse already includes it):

cp_rattata <- "Rattata" %>% ichooseyou(spread = 13)
cp <- c(cp_rattata, cp_rattata)

We read that as “Rattata! I choose you!” (well, only thirteen distinct colors, hence spread = 13, but you get the point).

If we’re not trying to choose a color palette, we read the %>% operator as ‘then’, but more on that below.

Let’s now take a look at how our dataset looks:

head(data)

As the dataset’s website explains, this a dataset containing 13 variables:


Verbs for columns: select() and mutate()

Selecting columns using select()

The first dplyr verb we’ll use is select(). It allows us to select only columns that we’re interested in, without creating subsets of the dataset or losing information. Let’s suppose that we want to visualize only the Number of the Pokemon, its Name and whether or not it is Legendary:

data %>%
  select(Number, Name, Legendary)

The verb select() also allows to choose columns by number:

data %>%
  select(1:2,13)

Mutating columns using mutate()

There is one column called Total which is described as “sum of all stats that come after this, a general guide to how strong a pokemon is.” Let’s verify this information summing up all the stats to mutate() this information into a new variable called Total2:

data %>%
  mutate(Total2 = HitPoints + Attack + Defense + SpecialAttack + SpecialDefense + Speed)

As we can see, mutate() makes it easy to work with the information contained in variables in order to create a completely new variable.


Verbs for rows: filter() and arrange()

Filtering rows with filter()

Which Pokemons are water type? Which are fire type? We can find out by using filter().

data %>%
  filter(Type1 == "Water")
data %>%
  filter(Type1 == "Fire")

Arranging information using arrange()

When we used select() to see the Pokemon Number, its Name and whether or not they are Legendary, we could only see FALSE results in the beginning. How about we re-arrange() the information to see those which are Legendary first?

data %>%
  select(Number, Name, Legendary) %>%
  arrange()

Wait, we wanted the TRUE values in Legendary to come first. By default, arrange() shows the information in alphabetical order a-z, or number order from lowest to highest. When showing booleans, it relies on FALSE = 0 and TRUE = 1, which means that shows FALSE first by default. If we want to reverse this and show results in descending order, we have to use desc(). So, no problem! We can ask arrange() to show results in descending order by including desc() on Legendary:

data %>%
  select(Number, Name, Legendary) %>%
  arrange(desc(Legendary))

Other verbs: summarise() and group_by()

There are at least two other verbs in dplyr which are quite useful. If we want to get summary statistics, we can use summarise() + the summarizing function we need. Plus, sometimes we need to analyze data by groups. This is where group_by() comes into play. Let’s use these two verbs at once to get the mean() and standard deviation sd() of the Total by Type1 of Pokemon, as well as how many there are by using n(), and then using arrange() to see which types are on the first positions:

data %>%
  group_by(Type1) %>%
  summarise(n = n(),
            avg_total = mean(Total),
            sd_total = sd(Total)) %>%
  arrange(desc(avg_total))

Dragon type is the best!

How many Pokemons are per type?

Let’s use some dplyr functions and ggplot to create a barchart of Pokemon types!

data %>%
    count(Type1) %>%
    mutate(Type1 = forcats::fct_reorder(Type1, n, .desc = FALSE)) %>%
    ggplot(aes(x = Type1, y = n)) + 
      geom_bar(stat = 'identity', aes(fill = Type1)) + 
      my_theme + 
      coord_flip() + 
      scale_fill_manual(values = cp, guide = FALSE)

NA

Exercises

  1. Use the filter function to select only the water Pokemons and save it in an object called water.
  2. Do the same with the fire Pokemons and save it in an object called fire.
  3. Which type is more powerful? Calculate the average Total score of each type of Pokemon. Use na.rm = TRUE. Do not use the %>% operator.
  4. Try to get to the same result in one pipeline by using group_by, filter and summarize.

Solutions

  1. Use the filter function to select only the water Pokemons and save it in an object called water.
water <- data %>% 
  filter(Type1 == "Water")
water
  1. Do the same with the fire Pokemons and save it in an object called fire.
fire <- data %>% 
  filter(Type1 == "Fire")
fire
fire <- data %>% 
  filter(Type1 == "Fire")
fire
  1. Which type is more powerful? Calculate the average Total score of each type of Pokemon. Use na.rm = TRUE. Do not use the %>% operator.
mean(water$Total, na.rm = TRUE)
[1] 430.4554
mean(fire$Total, na.rm = TRUE)
[1] 458.0769
  1. Try to get to the same result in one pipeline by using filter, group_by and summarize.
data %>%
  filter(Type1 == "Water" | Type1 == "Fire") %>%
  group_by(Type1) %>%
  summarise(mean(Total, na.rm = TRUE))

Congrats! You’ve learned dplyr!

LS0tCnRpdGxlOiAiZHBseXIgZm9yIGV4cGxvcmluZyBQb2tlbW9uIGRhdGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiFbXShodHRwczovL21lZGlhLmdpcGh5LmNvbS9tZWRpYS9CaW04UFR4eUJ1cmZpL2dpcGh5LmdpZikKCkZpcnN0IG9mIGFsbCwgd2UgbmVlZCB0byBpbnN0YWxsIHNvbWUgcGFja2FnZXMuIFJlbWVtYmVyIHRoYXQgYGRwbHlyYCBsaXZlcyBpbiB0aGUgYHRpZHl2ZXJzZWA6CmBgYCB7ciBldmFsID0gRkFMU0UsIGVycm9yID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpIAppbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCmluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpCmluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKQpgYGAKCkFuZCBzaW5jZSB0aGlzIGlzIGEgUG9rZW1vbi1iYXNlZCBleGVyY2lzZSwgbGV0J3MgYWxzbyBpbnN0YWxsIHNvbWUgUG9rZW1vbi1yZWxhdGVkIGNvbG9yIHBhbGV0dGVzOgoKYGBgIHtyIGV2YWwgPSBGQUxTRSwgZXJyb3IgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygncGFsZXR0ZXRvd24nKQpgYGAKCkxldCdzIGxvYWQgYWxsIHBhY2thZ2VzOgpgYGAge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShwYWxldHRldG93bikKbGlicmFyeShoaWdoY2hhcnRlciwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkocnZlc3QpCmBgYAoKTm93LCBsZXQncyBsb2FkIHRoYXQgZnJlZWx5IGF2YWlsYWJsZSBbUG9rZW1vbiBkYXRhc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2FiY3Nkcy9wb2tlbW9uKSEKYGBge3J9CmRhdGFfZmlsZSA8LSAnaHR0cHM6Ly9hc3NldHMuZGF0YWNhbXAuY29tL3Byb2R1Y3Rpb24vY291cnNlXzE4MTUvZGF0YXNldHMvUG9rZW1vbi5jc3YnCmRhdGEgPC0gcmVhZF9jc3YoZGF0YV9maWxlKQpgYGAKCkFuZCBzb21lIG1vcmUgdGhpbmdzIGhhcHBlbmluZyB1bmRlciBkYSBob29kOgpgYGAge3J9CnJfOTBfZCA8LSB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpjYXB0aW9uIDwtICJSTGFkaWVzIE11bmljaCIKbXlfdGhlbWUgPC0gdGhlbWVfZmV3KCkgCmBgYAoKV2UgYWxzbyBuZWVkIHNvbWUgUG9rZW1vbi0gYW5kIFJMYWRpZXMtIHJlbGF0ZWQgY29sb3JzLiBSYXR0YXRhIHNlZW1zIHRvIGhhdmUgYSBuaWNlIGNvbG9yIHNjaGVtZSBzaW1pbGFyIHRvIGJvdGguIFdlJ2xsIHVzZSB0aGUgYCU+JWAgKHBpcGUpIG9wZXJhdG9yIGZyb20gdGhlIGBtYWdyaXR0ZWAgcGFja2FnZSAoZG9uJ3Qgd29ycnkuIFRoZSBgdGlkeXZlcnNlYCBhbHJlYWR5IGluY2x1ZGVzIGl0KToKYGBgIHtyfQpjcF9yYXR0YXRhIDwtICJSYXR0YXRhIiAlPiUgaWNob29zZXlvdShzcHJlYWQgPSAxMykKY3AgPC0gYyhjcF9yYXR0YXRhLCBjcF9yYXR0YXRhKQpgYGAKCldlIHJlYWQgdGhhdCBhcyAiUmF0dGF0YSEgSSBjaG9vc2UgeW91ISIgKHdlbGwsIG9ubHkgdGhpcnRlZW4gZGlzdGluY3QgY29sb3JzLCBoZW5jZSBgc3ByZWFkID0gMTNgLCBidXQgeW91IGdldCB0aGUgcG9pbnQpLgoKSWYgd2UncmUgbm90IHRyeWluZyB0byBjaG9vc2UgYSBjb2xvciBwYWxldHRlLCB3ZSByZWFkIHRoZSBgJT4lYCBvcGVyYXRvciBhcyAndGhlbicsIGJ1dCBtb3JlIG9uIHRoYXQgYmVsb3cuCgpMZXQncyBub3cgdGFrZSBhIGxvb2sgYXQgaG93IG91ciBkYXRhc2V0IGxvb2tzOgpgYGB7cn0KaGVhZChkYXRhKQpgYGAKCkFzIHRoZSBbZGF0YXNldCdzIHdlYnNpdGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYWJjc2RzL3Bva2Vtb24pIGV4cGxhaW5zLCB0aGlzIGEgZGF0YXNldCBjb250YWluaW5nIDEzIHZhcmlhYmxlczoKCiogKipOdW1iZXIqKjogSUQgZm9yIGVhY2ggcG9rZW1vbgoqICoqTmFtZSoqOiBOYW1lIG9mIGVhY2ggcG9rZW1vbgoqICoqVHlwZTEqKjogRWFjaCBwb2tlbW9uIGhhcyBhIHR5cGUsIHRoaXMgZGV0ZXJtaW5lcyB3ZWFrbmVzcy9yZXNpc3RhbmNlIHRvIGF0dGFja3MKKiAqKlR5cGUyKio6IFNvbWUgcG9rZW1vbiBhcmUgZHVhbCB0eXBlIGFuZCBoYXZlIDIKKiAqKlRvdGFsKio6IHN1bSBvZiBhbGwgc3RhdHMgdGhhdCBjb21lIGFmdGVyIHRoaXMsIGEgZ2VuZXJhbCBndWlkZSB0byBob3cgc3Ryb25nIGEgcG9rZW1vbiBpcwoqICoqSGl0UG9pbnRzKio6IGhpdCBwb2ludHMsIG9yIGhlYWx0aCwgZGVmaW5lcyBob3cgbXVjaCBkYW1hZ2UgYSBwb2tlbW9uIGNhbiB3aXRoc3RhbmQgYmVmb3JlIGZhaW50aW5nCiogKipBdHRhY2sqKjogdGhlIGJhc2UgbW9kaWZpZXIgZm9yIG5vcm1hbCBhdHRhY2tzIChlZy4gU2NyYXRjaCwgUHVuY2gpCiogKipEZWZlbnNlKio6IHRoZSBiYXNlIGRhbWFnZSByZXNpc3RhbmNlIGFnYWluc3Qgbm9ybWFsIGF0dGFja3MKKiAqKlNwZWNpYWxBdHRhY2sqKjogc3BlY2lhbCBhdHRhY2ssIHRoZSBiYXNlIG1vZGlmaWVyIGZvciBzcGVjaWFsIGF0dGFja3MgKGUuZy4gZmlyZSBibGFzdCwgYnViYmxlIGJlYW0pCiogKipTcGVjaWFsRGVmZW5zZSoqOiB0aGUgYmFzZSBkYW1hZ2UgcmVzaXN0YW5jZSBhZ2FpbnN0IHNwZWNpYWwgYXR0YWNrcwoqICoqU3BlZWQqKjogZGV0ZXJtaW5lcyB3aGljaCBwb2tlbW9uIGF0dGFja3MgZmlyc3QgZWFjaCByb3VuZAoqICoqR2VuZXJhdGlvbioqOiB0aGUgbnVtYmVyIG9mIHRoZSBnZW5lcmF0aW9uIChhcyBhbiBpbnRlZ2VyKSBlYWNoIHBva2Vtb24gYmVsb25ncyB0by4KKiAqKkxlZ2VuZGFyeSoqOiB3aGV0aGVyIHRoZSBwb2tlbW9uIGlzIGxlZ2VuZGFyeSBvciBub3QsIGFzIGEgYm9vbGVhbiB2YWx1ZS4KCi0tLQoKIyMgVmVyYnMgZm9yIGNvbHVtbnM6IGBzZWxlY3QoKWAgYW5kIGBtdXRhdGUoKWAKIyMjIFNlbGVjdGluZyBjb2x1bW5zIHVzaW5nIGBzZWxlY3QoKWAKVGhlIGZpcnN0IGBkcGx5cmAgdmVyYiB3ZSdsbCB1c2UgaXMgYHNlbGVjdCgpYC4gSXQgYWxsb3dzIHVzIHRvIHNlbGVjdCBvbmx5IGNvbHVtbnMgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluLCB3aXRob3V0IGNyZWF0aW5nIHN1YnNldHMgb2YgdGhlIGRhdGFzZXQgb3IgbG9zaW5nIGluZm9ybWF0aW9uLiBMZXQncyBzdXBwb3NlIHRoYXQgd2Ugd2FudCB0byB2aXN1YWxpemUgb25seSB0aGUgYE51bWJlcmAgb2YgdGhlIFBva2Vtb24sIGl0cyBgTmFtZWAgYW5kIHdoZXRoZXIgb3Igbm90IGl0IGlzIGBMZWdlbmRhcnlgOgpgYGAge3J9CmRhdGEgJT4lCiAgc2VsZWN0KE51bWJlciwgTmFtZSwgTGVnZW5kYXJ5KQpgYGAKClRoZSB2ZXJiIGBzZWxlY3QoKWAgYWxzbyBhbGxvd3MgdG8gY2hvb3NlIGNvbHVtbnMgYnkgbnVtYmVyOgpgYGAge3J9CmRhdGEgJT4lCiAgc2VsZWN0KDE6MiwgMTMpCmBgYAoKCiFbXShodHRwczovL21lZGlhLnRlbm9yLmNvbS9pbWFnZXMvYzczZDZiYTdkNWRiNzJiNWYxMmU1MWU0ZTdlMWQ0NTUvdGVub3IuZ2lmKQoKCiMjIyBNdXRhdGluZyBjb2x1bW5zIHVzaW5nIGBtdXRhdGUoKWAKVGhlcmUgaXMgb25lIGNvbHVtbiBjYWxsZWQgYFRvdGFsYCB3aGljaCBpcyBkZXNjcmliZWQgYXMgInN1bSBvZiBhbGwgc3RhdHMgdGhhdCBjb21lIGFmdGVyIHRoaXMsIGEgZ2VuZXJhbCBndWlkZSB0byBob3cgc3Ryb25nIGEgcG9rZW1vbiBpcy4iIExldCdzIHZlcmlmeSB0aGlzIGluZm9ybWF0aW9uIHN1bW1pbmcgdXAgYWxsIHRoZSBzdGF0cyB0byBgbXV0YXRlKClgIHRoaXMgaW5mb3JtYXRpb24gaW50byBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYFRvdGFsMmA6CmBgYHtyfQpkYXRhICU+JQogIG11dGF0ZShUb3RhbDIgPSBIaXRQb2ludHMgKyBBdHRhY2sgKyBEZWZlbnNlICsgU3BlY2lhbEF0dGFjayArIFNwZWNpYWxEZWZlbnNlICsgU3BlZWQpCmBgYAoKQXMgd2UgY2FuIHNlZSwgYG11dGF0ZSgpYCBtYWtlcyBpdCBlYXN5IHRvIHdvcmsgd2l0aCB0aGUgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHZhcmlhYmxlcyBpbiBvcmRlciB0byBjcmVhdGUgYSBjb21wbGV0ZWx5IG5ldyB2YXJpYWJsZS4KCi0tLQoKIyMgVmVyYnMgZm9yIHJvd3M6IGBmaWx0ZXIoKWAgYW5kIGBhcnJhbmdlKClgCiMjIyBGaWx0ZXJpbmcgcm93cyB3aXRoIGBmaWx0ZXIoKWAKV2hpY2ggUG9rZW1vbnMgYXJlIHdhdGVyIHR5cGU/IFdoaWNoIGFyZSBmaXJlIHR5cGU/IFdlIGNhbiBmaW5kIG91dCBieSB1c2luZyBgZmlsdGVyKClgLgoKYGBge3J9CmRhdGEgJT4lCiAgZmlsdGVyKFR5cGUxID09ICJXYXRlciIpCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZmlsdGVyKFR5cGUxID09ICJGaXJlIikKYGBgCgoKIyMjIEFycmFuZ2luZyBpbmZvcm1hdGlvbiB1c2luZyBgYXJyYW5nZSgpYApXaGVuIHdlIHVzZWQgYHNlbGVjdCgpYCB0byBzZWUgdGhlIFBva2Vtb24gYE51bWJlcmAsIGl0cyBgTmFtZWAgYW5kIHdoZXRoZXIgb3Igbm90IHRoZXkgYXJlIGBMZWdlbmRhcnlgLCB3ZSBjb3VsZCBvbmx5IHNlZSBgRkFMU0VgIHJlc3VsdHMgaW4gdGhlIGJlZ2lubmluZy4gSG93IGFib3V0IHdlIHJlLWBhcnJhbmdlKClgIHRoZSBpbmZvcm1hdGlvbiB0byBzZWUgdGhvc2Ugd2hpY2ggYXJlIGBMZWdlbmRhcnlgIGZpcnN0PwoKYGBgIHtyfQpkYXRhICU+JQogIHNlbGVjdChOdW1iZXIsIE5hbWUsIExlZ2VuZGFyeSkgJT4lCiAgYXJyYW5nZSgpCmBgYAoKV2FpdCwgd2Ugd2FudGVkIHRoZSBgVFJVRWAgdmFsdWVzIGluIGBMZWdlbmRhcnlgIHRvIGNvbWUgZmlyc3QuIEJ5IGRlZmF1bHQsIGBhcnJhbmdlKClgIHNob3dzIHRoZSBpbmZvcm1hdGlvbiBpbiBhbHBoYWJldGljYWwgb3JkZXIgYS16LCBvciBudW1iZXIgb3JkZXIgZnJvbSBsb3dlc3QgdG8gaGlnaGVzdC4gV2hlbiBzaG93aW5nIGJvb2xlYW5zLCBpdCByZWxpZXMgb24gYEZBTFNFID0gMGAgYW5kIGBUUlVFID0gMWAsIHdoaWNoIG1lYW5zIHRoYXQgc2hvd3MgYEZBTFNFYCBmaXJzdCBieSBkZWZhdWx0LiBJZiB3ZSB3YW50IHRvIHJldmVyc2UgdGhpcyBhbmQgc2hvdyByZXN1bHRzIGluIGRlc2NlbmRpbmcgb3JkZXIsIHdlIGhhdmUgdG8gdXNlIGBkZXNjKClgLiBTbywgbm8gcHJvYmxlbSEgV2UgY2FuIGFzayBgYXJyYW5nZSgpYCB0byBzaG93IHJlc3VsdHMgaW4gZGVzY2VuZGluZyBvcmRlciBieSBpbmNsdWRpbmcgYGRlc2MoKWAgb24gYExlZ2VuZGFyeWA6CgpgYGB7cn0KZGF0YSAlPiUKICBzZWxlY3QoTnVtYmVyLCBOYW1lLCBMZWdlbmRhcnkpICU+JQogIGFycmFuZ2UoZGVzYyhMZWdlbmRhcnkpKQpgYGAKCiMjIE90aGVyIHZlcmJzOiBgc3VtbWFyaXNlKClgIGFuZCBgZ3JvdXBfYnkoKWAKVGhlcmUgYXJlIGF0IGxlYXN0IHR3byBvdGhlciB2ZXJicyBpbiBgZHBseXJgIHdoaWNoIGFyZSBxdWl0ZSB1c2VmdWwuIElmIHdlIHdhbnQgdG8gZ2V0IHN1bW1hcnkgc3RhdGlzdGljcywgd2UgY2FuIHVzZSBgc3VtbWFyaXNlKClgICsgdGhlIHN1bW1hcml6aW5nIGZ1bmN0aW9uIHdlIG5lZWQuIFBsdXMsIHNvbWV0aW1lcyB3ZSBuZWVkIHRvIGFuYWx5emUgZGF0YSBieSBncm91cHMuIFRoaXMgaXMgd2hlcmUgYGdyb3VwX2J5KClgIGNvbWVzIGludG8gcGxheS4gTGV0J3MgdXNlIHRoZXNlIHR3byB2ZXJicyBhdCBvbmNlIHRvIGdldCB0aGUgYG1lYW4oKWAgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBgc2QoKWAgb2YgdGhlIGBUb3RhbGAgYnkgYFR5cGUxYCBvZiBQb2tlbW9uLCBhcyB3ZWxsIGFzIGhvdyBtYW55IHRoZXJlIGFyZSBieSB1c2luZyBgbigpYCwgYW5kIHRoZW4gdXNpbmcgYGFycmFuZ2UoKWAgdG8gc2VlIHdoaWNoIHR5cGVzIGFyZSBvbiB0aGUgZmlyc3QgcG9zaXRpb25zOgoKYGBgIHtyfQpkYXRhICU+JQogIGdyb3VwX2J5KFR5cGUxKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSwKICAgICAgICAgICAgYXZnX3RvdGFsID0gbWVhbihUb3RhbCksCiAgICAgICAgICAgIHNkX3RvdGFsID0gc2QoVG90YWwpKSAlPiUKICBhcnJhbmdlKGRlc2MoYXZnX3RvdGFsKSkKYGBgCgojIERyYWdvbiB0eXBlIGlzIHRoZSBiZXN0IQoKIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhL0hqbTl4ZmFReWlCQ0UvZ2lwaHkuZ2lmKQoKIyMjIEhvdyBtYW55IFBva2Vtb25zIGFyZSBwZXIgdHlwZT8KTGV0J3MgdXNlIHNvbWUgYGRwbHlyYCBmdW5jdGlvbnMgYW5kIGBnZ3Bsb3RgIHRvIGNyZWF0ZSBhIGJhcmNoYXJ0IG9mIFBva2Vtb24gdHlwZXMhCgpgYGB7cn0KZGF0YSAlPiUKICAgIGNvdW50KFR5cGUxKSAlPiUKICAgIG11dGF0ZShUeXBlMSA9IGZvcmNhdHM6OmZjdF9yZW9yZGVyKFR5cGUxLCBuLCAuZGVzYyA9IEZBTFNFKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBUeXBlMSwgeSA9IG4pKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgYWVzKGZpbGwgPSBUeXBlMSkpICsgCiAgICAgIG15X3RoZW1lICsgCiAgICAgIGNvb3JkX2ZsaXAoKSArIAogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjcCwgZ3VpZGUgPSBGQUxTRSkKICAKYGBgCgoKIyBFeGVyY2lzZXMKIVtdKGh0dHBzOi8vNjgubWVkaWEudHVtYmxyLmNvbS9kMThkYjMzZGViMjFhZjQ3Y2QwZjliMTllZjZmOThiYS90dW1ibHJfbjQ0dWs4a1lPeTF0dGhobGhvMV81MDAuZ2lmKQoKMS4gVXNlIHRoZSBgZmlsdGVyYCBmdW5jdGlvbiB0byBzZWxlY3Qgb25seSB0aGUgd2F0ZXIgUG9rZW1vbnMgYW5kIHNhdmUgaXQgaW4gYW4gb2JqZWN0IGNhbGxlZCBgd2F0ZXJgLgoyLiBEbyB0aGUgc2FtZSB3aXRoIHRoZSBmaXJlIFBva2Vtb25zIGFuZCBzYXZlIGl0IGluIGFuIG9iamVjdCBjYWxsZWQgYGZpcmVgLgozLiBXaGljaCB0eXBlIGlzIG1vcmUgcG93ZXJmdWw/IENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBgVG90YWxgIHNjb3JlIG9mIGVhY2ggdHlwZSBvZiBQb2tlbW9uLiBVc2UgYG5hLnJtID0gVFJVRWAuIERvIG5vdCB1c2UgdGhlIGAlPiVgIG9wZXJhdG9yLgo0LiBUcnkgdG8gZ2V0IHRvIHRoZSBzYW1lIHJlc3VsdCBpbiBvbmUgcGlwZWxpbmUgYnkgdXNpbmcgYGdyb3VwX2J5YCwgYGZpbHRlcmAgYW5kIGBzdW1tYXJpemVgLgoKCiMgU29sdXRpb25zCjEuIFVzZSB0aGUgYGZpbHRlcmAgZnVuY3Rpb24gdG8gc2VsZWN0IG9ubHkgdGhlIHdhdGVyIFBva2Vtb25zIGFuZCBzYXZlIGl0IGluIGFuIG9iamVjdCBjYWxsZWQgYHdhdGVyYC4KYGBgIHtyIGVjaG8gPSBUUlVFfQp3YXRlciA8LSBkYXRhICU+JSAKICBmaWx0ZXIoVHlwZTEgPT0gIldhdGVyIikKCndhdGVyCmBgYAoKMi4gRG8gdGhlIHNhbWUgd2l0aCB0aGUgZmlyZSBQb2tlbW9ucyBhbmQgc2F2ZSBpdCBpbiBhbiBvYmplY3QgY2FsbGVkIGBmaXJlYC4KYGBgIHtyfQpmaXJlIDwtIGRhdGEgJT4lIAogIGZpbHRlcihUeXBlMSA9PSAiRmlyZSIpCmZpcmUKYGBgCmBgYCB7cn0KZmlyZSA8LSBkYXRhICU+JSAKICBmaWx0ZXIoVHlwZTEgPT0gIkZpcmUiKQpmaXJlCgoKYGBgCjMuIFdoaWNoIHR5cGUgaXMgbW9yZSBwb3dlcmZ1bD8gQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGBUb3RhbGAgc2NvcmUgb2YgZWFjaCB0eXBlIG9mIFBva2Vtb24uIFVzZSBgbmEucm0gPSBUUlVFYC4gRG8gbm90IHVzZSB0aGUgYCU+JWAgb3BlcmF0b3IuCmBgYCB7cn0KbWVhbih3YXRlciRUb3RhbCwgbmEucm0gPSBUUlVFKQptZWFuKGZpcmUkVG90YWwsIG5hLnJtID0gVFJVRSkKYGBgCgo0LiBUcnkgdG8gZ2V0IHRvIHRoZSBzYW1lIHJlc3VsdCBpbiBvbmUgcGlwZWxpbmUgYnkgdXNpbmcgYGZpbHRlcmAsIGBncm91cF9ieWAgYW5kIGBzdW1tYXJpemVgLgpgYGB7cn0KZGF0YSAlPiUKICBmaWx0ZXIoVHlwZTEgPT0gIldhdGVyIiB8IFR5cGUxID09ICJGaXJlIikgJT4lCiAgZ3JvdXBfYnkoVHlwZTEpICU+JQogIHN1bW1hcmlzZShtZWFuKFRvdGFsLCBuYS5ybSA9IFRSVUUpKQpgYGAKCiMjIENvbmdyYXRzISBZb3UndmUgbGVhcm5lZCBkcGx5ciEKIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhL3loZlRZOEpMMXdJQUUvZ2lwaHkuZ2lmKQ==